1 module d_tree_sitter.tree;
2 
3 extern (C):
4 
5 import d_tree_sitter.language;
6 import d_tree_sitter.node;
7 import d_tree_sitter.tree_visitor;
8 import d_tree_sitter.other;
9 import d_tree_sitter.libc : TSTree;
10 
11 /** A tree that represents the syntactic structure of a source code file. */
12 struct Tree
13 {
14   import d_tree_sitter.libc : ts_tree_delete, ts_tree_root_node, ts_tree_language,
15     ts_tree_edit, ts_tree_get_changed_ranges, ts_tree_copy, ts_tree_print_dot_graph;
16 
17   import std.stdio : File;
18 
19   /** internal TsTree */
20   TSTree* tstree;
21 
22   /** Create a new Tree */
23   this(TSTree* tstree) @nogc nothrow
24   {
25     assert(tstree != null, "The given tstree is null");
26     this.tstree = tstree;
27   }
28 
29   ~this() @nogc nothrow
30   {
31     ts_tree_delete(this.tstree);
32   }
33 
34   /**
35    * Create a shallow copy of the syntax tree. This is very fast.
36    *
37    * You need to copy a syntax tree in order to use it on more than one thread at
38    * a time, as syntax trees are not thread safe.
39    */
40   this(ref return scope Tree otherTree) @nogc nothrow
41   {
42     this.tstree = ts_tree_copy(otherTree.tstree);
43   }
44 
45   /// ditto
46   this(ref return scope inout Tree otherTree) @nogc nothrow inout
47   {
48     this.tstree = cast(inout(TSTree*)) ts_tree_copy(otherTree.tstree);
49   }
50 
51   @disable this(this); // disable postblit
52 
53   /** Create an empty Tree */
54   static auto create_empty() @nogc nothrow
55   {
56     return cast(const(TSTree)*) null;
57   }
58 
59   /**  Get the root node of the syntax tree. */
60   auto root_node() const @nogc nothrow
61   {
62     return Node(ts_tree_root_node(tstree));
63   }
64 
65   /**  Get the language that was used to parse the syntax tree. */
66   auto language() const @nogc nothrow
67   {
68     return Language(ts_tree_language(tstree));
69   }
70 
71   /** Edit the syntax tree to keep it in sync with source code that has been
72       edited.
73 
74       You must describe the edit both in terms of byte offsets and in terms of
75       row/column coordinates.
76   */
77   auto edit(const InputEdit* edit) @nogc nothrow
78   {
79     return ts_tree_edit(tstree, edit);
80   }
81 
82   /**  Create a new [TreeCursor] starting from the root of the tree. */
83   auto walk() const @nogc nothrow
84   {
85     return root_node().walk();
86   }
87 
88   /**
89     Traverse the [Tree] starting from its root [Node] applying a visitor at all nodes.
90   */
91   void traverse(TreeVisitor visitor) const
92   {
93     root_node().traverse(visitor);
94   }
95 
96   /**
97     Traverse the [Tree] starting from its root [Node] applying a visitor at all nodes.
98 
99     NOTE: if you are sure that TreeVisitor is nothrow, you can use this method
100   */
101   void traverse_nothrow(TreeVisitor visitor) const
102   {
103     root_node().traverse_nothrow(visitor);
104   }
105 
106   /**  Compare this old edited syntax tree to a new syntax tree representing the same
107       document, returning a sequence of ranges whose syntactic structure has changed.
108 
109       For this to work correctly, this syntax tree must have been edited such that its
110       ranges match up to the new tree. Generally, youl want to call this method right
111       after calling one of the [Parser::parse] functions. Call it on the old tree that
112       was passed to parse, and pass the new tree that was returned from `parse`.
113   */
114   auto changed_ranges(Tree other) const nothrow
115   {
116     auto count = 0u;
117     const auto ptr = ts_tree_get_changed_ranges(tstree, other.tstree, &count);
118     // TODO ptr is not freed!
119     // TODO is there a better way to convert this to an array?
120     Range[] ranges;
121     ranges.reserve(count);
122     for (auto iptr = 0u; iptr < count; iptr++)
123     {
124       ranges[iptr] = *(ptr + iptr);
125     }
126     return ranges;
127   }
128 
129   /**
130    * Write a DOT graph describing the syntax tree to the given file.
131    */
132   void print_dot_graph(File file) const
133   {
134     ts_tree_print_dot_graph(tstree, file.getFP);
135   }
136 
137   /**
138     Get a DOT graph describing the syntax tree as a string
139    */
140   auto dot_graph() const
141   {
142     import std.file : readText, tempDir;
143     import std.path : buildPath;
144 
145     // TODO do we need to create a temp file?
146     const fileName = buildPath(tempDir(), "tree_sitter_dot_graph.txt");
147     auto file = File(fileName, "w");
148     print_dot_graph(file);
149     file.close();
150     return readText(fileName);
151   }
152 }